package com.youlu.campus.service.order.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.exception.BusinessException;
import com.youlu.campus.entity.DeliveryOrderItem;
import com.youlu.campus.entity.DeliveryOrderTask;
import com.youlu.campus.entity.ExpressCompany;
import com.youlu.campus.entity.FileExportTask;
import com.youlu.campus.entity.VO.req.DeliveryOrderQueryVO;
import com.youlu.campus.entity.VO.req.DeliveryOrderRequestVO;
import com.youlu.campus.entity.VO.req.ImportDeliveryOrderExcel;
import com.youlu.campus.entity.VO.req.ImportExpressCompany;
import com.youlu.campus.entity.VO.res.ExportSFDeliveryOrderVO;
import com.youlu.campus.entity.enums.ExpressWay;
import com.youlu.campus.service.file.FileExportTaskService;
import com.youlu.campus.service.file.FileService;
import com.youlu.campus.service.order.EMSService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.bson.types.ObjectId;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service
public class DeliveryOrderService {

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private DeliveryOrderItemService deliveryOrderItemService;

    @Autowired
    private FileExportTaskService fileExportTaskService;

    @Autowired
    private FileService fileService;

    @Autowired
    private EMSService emsService;




    public Boolean exportItem(DeliveryOrderQueryVO deliveryOrder){
        FileExportTask fileExportTask = new FileExportTask();
        fileExportTask.setToken(deliveryOrder.getOptUserName());
        fileExportTask.setBizType("order");
        fileExportTask.setStatus(0);
        fileExportTask.setOptUser(deliveryOrder.getOptUserName());
        fileExportTask.setOptUserId(deliveryOrder.getOptUserId());
        String fileName = "发货订单明细" + new Date().getTime() + ExcelTypeEnum.XLSX.getValue();
        fileExportTask.setFileName(fileName);
        Query query = getQueryItem(deliveryOrder);
        long count = this.mongoTemplate.count(query, DeliveryOrderItem.class);
        if(count==0){
            log.error("导出数据未空");
            return Boolean.FALSE;
        }
        fileExportTask.setNum((int)count);
        String fileExportId = fileExportTaskService.create(fileExportTask);
        query.limit(100000);
        List<DeliveryOrderItem> itemList = this.mongoTemplate.find(query, DeliveryOrderItem.class);
        List<ExportSFDeliveryOrderVO> exportSFList = itemList.stream().map(deliveryOrderItem -> {
            ExportSFDeliveryOrderVO exportSFDeliveryOrder = new ExportSFDeliveryOrderVO();
            BeanUtils.copyProperties(deliveryOrderItem, exportSFDeliveryOrder);
            exportSFDeliveryOrder.setMailNo(deliveryOrderItem.getExpressNumber());
            exportSFDeliveryOrder.setSubOrderNo(" " + deliveryOrderItem.getSubOrderNo());
            exportSFDeliveryOrder.setReceiveMobile(" " + deliveryOrderItem.getReceiveMobile());
            return exportSFDeliveryOrder;
        }).collect(Collectors.toList());
        ExcelWriter excelWriter = null;
        InputStream in = null;
        String filePath = "/data/app/file/"+fileName;
        try {
            excelWriter = EasyExcel.write(filePath, ExportSFDeliveryOrderVO.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("").build();
            excelWriter.write(exportSFList, writeSheet);
        }catch (Exception e){
         log.error("导出失败",e);
            return Boolean.FALSE;
        }finally {
            if(Objects.nonNull(excelWriter)){
                excelWriter.finish();
            }
            if(Objects.nonNull(in)){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        File file = new File(filePath);
        if(!file.exists()){
            log.error("文件不存在");
            return Boolean.FALSE;
        }
        try {
            in = new FileInputStream(file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        String fileUrl = fileService.uploadFile(in, "发货订单明细"+ExcelTypeEnum.XLSX.getValue());
        Query queryTask = new Query().addCriteria(Criteria.where("id").is(fileExportId));
        Update update = new Update();
        update.set("status", 1);
        update.set("url", fileUrl);
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(queryTask, update, FileExportTask.class);
        log.info("更新文件内容状态-> {} ",updateResult.getModifiedCount());
        return Boolean.TRUE;

    }

    public void syncItem(String itemId){
        DeliveryOrderItem deliveryOrderItem = this.mongoTemplate.findById(itemId, DeliveryOrderItem.class);
        if(Objects.isNull(deliveryOrderItem)){
            throw new BusinessException("任务项不为空");
        }
        if(DeliveryOrderItem.SUCCESS.equals(deliveryOrderItem.getStatus())){
            throw new BusinessException("已经推送成功，不能重复发送");
        }
        if(ExpressWay.SF.equals(deliveryOrderItem.getExpressWay())){
            deliveryOrderItemService.doSyncSF(deliveryOrderItem);
        }else{
            emsService.doEms(deliveryOrderItem);
        }

    }

    public void sync(String id){
        DeliveryOrderTask deliveryOrderTask = this.mongoTemplate.findById(id, DeliveryOrderTask.class);
        if(Objects.isNull(deliveryOrderTask)){
            return;
        }
        log.info("开始同步发货单任务->{}",deliveryOrderTask.getTaskName());
        if(DeliveryOrderTask.START.equals(deliveryOrderTask.getStatus())){
            throw new BusinessException("正在同步中,请勿重复操作");
        }
        deliveryOrderTask.setStartTime(new Date());
        deliveryOrderTask.setStatus(DeliveryOrderTask.START);
        this.mongoTemplate.save(deliveryOrderTask);
        Integer pageNo =1;
        Integer pageSize =1000;

        while (true){
            Query query = new Query();
            query.addCriteria(Criteria.where("taskId").is(id));
            PageRequest pageRequest = PageRequest.of(pageNo - 1, pageSize);
            query.with(Sort.by(Sort.Direction.ASC,"batchNo"));
            List<DeliveryOrderItem> list = this.mongoTemplate.find(query.with(pageRequest), DeliveryOrderItem.class);
            if(CollectionUtils.isEmpty(list)){
                break;
            }
            list.stream().forEach(deliveryOrderItem -> {
                if(ExpressWay.SF.equals(deliveryOrderItem.getExpressWay())){
                    deliveryOrderItemService.doSyncSF(deliveryOrderItem);
                }else{
                    emsService.doEms(deliveryOrderItem);
                }
            });
            pageNo ++;
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        Update update = new Update();
        update.set("status",DeliveryOrderTask.FINISHED);
        update.set("updatedTime",new Date());
        this.mongoTemplate.updateFirst(query,update,DeliveryOrderTask.class);
    }

    public DeliveryOrderTask detail(String id){
        return this.mongoTemplate.findById(id,DeliveryOrderTask.class);
    }

    public void delete(String id){
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        this.mongoTemplate.remove(query,DeliveryOrderTask.class);
        query = new Query();
        query.addCriteria(Criteria.where("taskId").is(id));
        this.mongoTemplate.remove(query,DeliveryOrderItem.class);

    }

    private Query getQueryItem(DeliveryOrderQueryVO deliveryOrder){
        Query query = new Query();
        if(StringUtils.isNotBlank(deliveryOrder.getTaskId())){
            query.addCriteria(Criteria.where("taskId").is(deliveryOrder.getTaskId()));
        }
        String expressCompanyId = deliveryOrder.getExpressCompanyId();
        if(StringUtils.isNotBlank(expressCompanyId)){
            query.addCriteria(Criteria.where("expressCompanyId").is(expressCompanyId));
        }
        String receiveMobile = deliveryOrder.getReceiveMobile();
        if(StringUtils.isNotBlank(receiveMobile)){
            query.addCriteria(Criteria.where("receiveMobile").is(receiveMobile));
        }
        String receiveName = deliveryOrder.getReceiveName();
        if(StringUtils.isNotBlank(receiveName)){
            Pattern pattern = Pattern.compile("^.*" + receiveName + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("receiveName").regex(pattern));
        }
        String status = deliveryOrder.getStatus();
        if(StringUtils.isNotBlank(status)){
            query.addCriteria(Criteria.where("status").is(status));
        }
        String deliveryStatus = deliveryOrder.getDeliveryStatus();
        if(StringUtils.isNotBlank(deliveryStatus)){
            query.addCriteria(Criteria.where("deliveryStatus").is(deliveryStatus));
        }
        return query;
    }

    public Page<DeliveryOrderItem> itemList(DeliveryOrderQueryVO deliveryOrder){
        Query query = getQueryItem(deliveryOrder);
        long count = this.mongoTemplate.count(query, DeliveryOrderItem.class);
        PageRequest pageRequest = PageRequest.of(deliveryOrder.getPageNo() - 1, deliveryOrder.getPageSize());
        if (count == 0) {
            return PageableExecutionUtils.getPage(new ArrayList<>(), pageRequest, () -> count);
        }
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<DeliveryOrderItem> list = this.mongoTemplate.find(query.with(pageRequest), DeliveryOrderItem.class);
        return PageableExecutionUtils.getPage(list, pageRequest, () -> count);
    }


    public Page<ExpressCompany> expressList(DeliveryOrderQueryVO deliveryOrder){
        Query query = new Query();
        if(StringUtils.isNotBlank(deliveryOrder.getExpressCompanyName())){
            Pattern pattern = Pattern.compile("^.*" + deliveryOrder.getExpressCompanyName() + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("name").regex(pattern));
        }
        long count = this.mongoTemplate.count(query, ExpressCompany.class);
        PageRequest pageRequest = PageRequest.of(deliveryOrder.getPageNo() - 1, deliveryOrder.getPageSize());
        if (count == 0) {
            return PageableExecutionUtils.getPage(new ArrayList<>(), pageRequest, () -> count);
        }
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<ExpressCompany> list = this.mongoTemplate.find(query.with(pageRequest), ExpressCompany.class);
        return PageableExecutionUtils.getPage(list, pageRequest, () -> count);
    }

    public Page<DeliveryOrderTask> list(DeliveryOrderQueryVO deliveryOrder){
        Query query = new Query();
        String taskName = deliveryOrder.getTaskName();
        if(StringUtils.isNotBlank(taskName)){
            Pattern pattern = Pattern.compile("^.*" + taskName + ".*$", Pattern.CASE_INSENSITIVE);
            query.addCriteria(Criteria.where("taskName").regex(pattern));
        }
        long count = this.mongoTemplate.count(query, DeliveryOrderTask.class);
        PageRequest pageRequest = PageRequest.of(deliveryOrder.getPageNo() - 1, deliveryOrder.getPageSize());
        if (count == 0) {
            return PageableExecutionUtils.getPage(new ArrayList<>(), pageRequest, () -> count);
        }
//        query.with(new Sort(Sort.Direction.DESC, "createdTime"));
        Sort sort=Sort.by(Sort.Direction.DESC, "createdTime");
        query.with(sort);
        List<DeliveryOrderTask> list = this.mongoTemplate.find(query.with(pageRequest), DeliveryOrderTask.class);
        return PageableExecutionUtils.getPage(list, pageRequest, () -> count);
    }

    public void importExpressCompany(List<ImportExpressCompany> excelDataList) {
        if (CollectionUtils.isEmpty(excelDataList)) {
            return;
        }
        List<ExpressCompany> list = excelDataList.stream().map(excelExpress -> {
            ExpressCompany expressCompany = new ExpressCompany();
            BeanUtils.copyProperties(excelExpress, expressCompany);
            return expressCompany;
        }).collect(Collectors.toList());
        this.mongoTemplate.insertAll(list);
    }

    public void importDeliveryOrder(DeliveryOrderRequestVO deliveryOrder,
                                    List<ImportDeliveryOrderExcel> deliveryOrderExcel) {
        log.info("开始导入发货单任务-> {}", deliveryOrder.getTaskName());
        String taskId = deliveryOrder.getTaskId();
        if (StringUtils.isBlank(taskId) && CollectionUtils.isEmpty(deliveryOrderExcel)) {
            throw new BusinessException("发货清单不能为空");
        }
        DeliveryOrderTask deliveryOrderTask = null;
        Date now = new Date();
        if (StringUtils.isNotBlank(taskId)) {
            deliveryOrderTask = mongoTemplate.findById(taskId, DeliveryOrderTask.class);
            deliveryOrderTask.setUpdatedTime(now);
        } else {
            deliveryOrderTask = new DeliveryOrderTask();
            deliveryOrderTask.setCreatedTime(now);
            taskId = new ObjectId().toString();
            deliveryOrderTask.setId(taskId);
        }

        BeanUtils.copyProperties(deliveryOrder, deliveryOrderTask);
        deliveryOrderTask.setExpressWay(deliveryOrder.getExpressWay());
        this.mongoTemplate.save(deliveryOrderTask);
        if (CollectionUtils.isNotEmpty(deliveryOrderExcel)) {
            Query query = new Query();
            query.addCriteria(Criteria.where("taskId").is(taskId));
            this.mongoTemplate.remove(query, DeliveryOrderItem.class);
            String finalTaskId = taskId;
            String DATA_FORMAT = "yyyyMMddHHmmss";
            LocalDateTime currentDate = LocalDateTime.now();
            //批次号
            String batchNo = currentDate.format(DateTimeFormatter.ofPattern(DATA_FORMAT));
            AtomicReference<LocalDateTime> plusTime = new AtomicReference<>();
            plusTime.getAndSet(currentDate);
            List<DeliveryOrderItem> batchDeliveryOrderItem = deliveryOrderExcel.stream().map(deliveryOrderItem -> {
                DeliveryOrderItem item = new DeliveryOrderItem();
                BeanUtils.copyProperties(deliveryOrderItem, item);
                item.setExpressCompanyId(deliveryOrder.getExpressCompanyId());
                item.setExpressCompanyCode(deliveryOrder.getExpressCompanyCode());
                item.setExpressCompanyName(deliveryOrder.getExpressCompanyName());
                item.setExpressWay(deliveryOrder.getExpressWay());
                item.setTaskId(finalTaskId);
                plusTime.getAndSet(plusTime.get().plus(1, ChronoUnit.SECONDS));
                item.setTimeEMS(currentDate);
                item.setBatchNo(plusTime.get().format(DateTimeFormatter.ofPattern(DATA_FORMAT)));
                return item;
            }).collect(Collectors.toList());
            this.mongoTemplate.insertAll(batchDeliveryOrderItem);
        }
    }

    public static void main(String[] args) {
        LocalDateTime currentDate = LocalDateTime.now();
        String DATA_FORMAT = "yyyyMMddHHmmss";
        AtomicReference<LocalDateTime> plusTime = new AtomicReference<LocalDateTime>();
        plusTime.getAndSet(currentDate);
        for(int i=0;i<10;i++){
            plusTime.getAndSet(plusTime.get().plus(1, ChronoUnit.SECONDS));
            log.info(plusTime.get().format(DateTimeFormatter.ofPattern(DATA_FORMAT)));
        }
    }
}
